#ifndef STX_TYPE_TRAITS
#define STX_TYPE_TRAITS

#include <stx/config.hpp>
#include <cstddef>
#include <stx/core/type_traits/remove_extent.hpp>
#include <stx/core/type_traits/remove_all_extents.hpp>
#include <stx/core/type_traits/add_lvalue_reference.hpp>

#ifdef STX_HAS_INCLASS_MEMBER_INITIALIZATION
#   define STX_STATIC_CONSTANT(type, variable_name, value) static const type variable_name = value
#else
#   define STX_STATIC_CONSTANT(type, variable_name, value) enum { variable_name = value }
#endif

namespace stx {

template <class T, T v>
struct integral_constant
{
    STX_STATIC_CONSTANT(T, value, v);
    typedef T value_type;
    typedef integral_constant type;
    
    operator value_type() const
    {
        return value;
    }
    
    static value_type get_value()
    {
        return value;
    }
};

typedef integral_constant<bool, true>  true_type;
typedef integral_constant<bool, false> false_type;

//  is_same
template <class T, class U> struct is_same: false_type {};
template <class T>          struct is_same<T, T>: true_type {};

//  remove_const
template <class T> struct remove_const          { typedef T type; };
template <class T> struct remove_const<const T> { typedef T type; };

//  remove_volatile
template <class T> struct remove_volatile             { typedef T type; };
template <class T> struct remove_volatile<volatile T> { typedef T type; };

//  remove_cv
template <class T>
struct remove_cv {
    typedef typename remove_volatile<typename remove_const<T>::type>::type type;
};

///////////////////////////////////////////////////////////////////////////////
//  Type properties
///////////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------------
//  Primary type categories
//-----------------------------------------------------------------------------

//  is_void
template <class T>
struct is_void: integral_constant<
     bool,
     is_same<void, typename remove_cv<T>::type>::value
> {};

//  is_integral
template <class T> struct is_integral_impl:                     public false_type {};
template <>        struct is_integral_impl<bool>:               public true_type {};
template <>        struct is_integral_impl<char>:               public true_type {};
template <>        struct is_integral_impl<signed char>:        public true_type {};
template <>        struct is_integral_impl<unsigned char>:      public true_type {};
#ifdef STX_HAS_WCHAR_T
template <>        struct is_integral_impl<wchar_t>:            public true_type {};
#endif
#ifdef STX_HAS_UNICODE_CHARS
template <>        struct is_integral_impl<char16_t>:           public true_type {};
template <>        struct is_integral_impl<char32_t>:           public true_type {};
#endif
template <>        struct is_integral_impl<short>:              public true_type {};
template <>        struct is_integral_impl<unsigned short>:     public true_type {};
template <>        struct is_integral_impl<int>:                public true_type {};
template <>        struct is_integral_impl<unsigned int>:       public true_type {};
template <>        struct is_integral_impl<long>:               public true_type {};
template <>        struct is_integral_impl<unsigned long>:      public true_type {};
#ifdef STX_HAS_LONG_LONG
template <>        struct is_integral_impl<long long>:          public true_type {};
template <>        struct is_integral_impl<unsigned long long>: public true_type {};
#endif

template <class T> struct is_integral: public is_integral_impl<typename remove_cv<T>::type> {};

//  is_floating_point
template <class T>
struct is_floating_point: integral_constant<
    bool,
    is_same<float, typename remove_cv<T>::type>::value ||
    is_same<double, typename remove_cv<T>::type>::value ||
    is_same<long double, typename remove_cv<T>::type>::value
> {};

//  is_array
template <class T>
struct is_array: false_type {};
 
template <class T>
struct is_array<T[]>: true_type {};
 
template <class T, std::size_t N>
struct is_array<T[N]>: true_type {};

//  is_pointer
template <class T> struct is_pointer_impl: false_type {};
template <class T> struct is_pointer_impl<T*>: true_type {};
template <class T> struct is_pointer: is_pointer_impl<typename remove_cv<T>::type> {};

//  is_lvalue_reference
template <class T> struct is_lvalue_reference: false_type {};
template <class T> struct is_lvalue_reference<T&>: true_type {};

//  is_rvalue_reference
template <class T> struct is_rvalue_reference: false_type {};
#ifdef STX_HAS_RVALUE_REFERENCES
template <class T> struct is_rvalue_reference<T&&>: true_type {};
#endif

//  is_member_object_pointer

//  is_member_function_pointer
//template <class T>
//struct is_member_function_pointer_helper: false_type {};
// 
//template <class T, class U>
//struct is_member_function_pointer_helper<T U::*>: is_function<T> {};
// 
//template <class T>
//struct is_member_function_pointer: is_member_function_pointer_helper<
//    typename remove_cv<T>::type
//> {};

//  is_enum

//  is_union

//  is_class

//  is_function

//-----------------------------------------------------------------------------
//  Composite type categories
//-----------------------------------------------------------------------------

//  is_reference
template <class T> struct is_reference: false_type {};
template <class T> struct is_reference<T&>: true_type {};
#ifdef STX_HAS_RVALUE_REFERENCES
template <class T> struct is_reference<T&&>: true_type {};
#endif

//  is_arithmetic
template <class T>
struct is_arithmetic: integral_constant<bool,
    is_integral<T>::value ||
    is_floating_point<T>::value
> {};

//  is_fundamental
template <class T>
struct is_fundamental: integral_constant<
    bool,
    is_arithmetic<T>::value || is_void<T>::value
#ifdef STX_HAS_NULLPTR_T
    || is_same<nullptr_t, typename remove_cv<T>::type>::value
#endif
> {};

//  is_object
//template <class T>
//struct is_object : integral_constant<bool,
//    is_scalar<T>::value ||
//    is_array<T>::value  ||
//    is_union<T>::value  ||
//    is_class<T>::value
//> {};

//  is_compound
template <class T>
struct is_compound: integral_constant<bool, !is_fundamental<T>::value> {};

//  is_member_pointer
template <class T>
struct is_member_pointer_impl: false_type {};
 
template <class T, class U>
struct is_member_pointer_impl<T U::*>: true_type {};
 
template <class T>
struct is_member_pointer: is_member_pointer_impl<typename remove_cv<T>::type> {};

//  is_scalar
template <class T>
struct is_scalar: integral_constant<bool,
    is_arithmetic<T>::value     ||
//    is_enum<T>::value           ||
    is_pointer<T>::value        ||
    is_member_pointer<T>::value
#ifdef STX_HAS_NULLPTR_T
    || is_same<nullptr_t, typename remove_cv<T>::type>::value
#endif
> {};

//-----------------------------------------------------------------------------
//  Type properties
//-----------------------------------------------------------------------------

//  is_const
template <class T> struct is_const: false_type {};
template <class T> struct is_const<const T>: true_type {};

//  is_volatile
template <class T> struct is_volatile: false_type {};
template <class T> struct is_volatile<volatile T>: true_type {};

//  is_trivial

//  is_trivially_copyable

//  is_standard_layout

//  is_pod

//  is_literal_type

//  is_empty

//  is_polymorphic

//  is_abstract

//  is_signed

//  is_unsigned

//-----------------------------------------------------------------------------
//  Supported operations
//-----------------------------------------------------------------------------

//  is_constructible

//  is_trivially_constructible

//  is_nothrow_constructible

//  is_default_constructible

//  is_trivially_default_constructible

//  is_nothrow_default_constructible

//  is_copy_constructible

//  is_trivially_copy_constructible

//  is_nothrow_copy_constructible

//  is_move_constructible

//  is_trivially_move_constructible

//  is_nothrow_move_constructible

//  is_assignable

//  is_trivially_assignable
template <class T, class Arg>
struct is_trivially_assignable: public false_type {};

template <class T>
struct is_trivially_assignable<T&, T>: integral_constant<bool, is_scalar<T>::value> {};

template <class T>
struct is_trivially_assignable<T&, T&>: integral_constant<bool, is_scalar<T>::value> {};

template <class T>
struct is_trivially_assignable<T&, const T&>: integral_constant<bool, is_scalar<T>::value> {};

#ifdef STX_HAS_RVALUE_REFERENCES

template <class T>
struct is_trivially_assignable<T&, T&&>: integral_constant<bool, is_scalar<T>::value> {};

#endif

//  is_nothrow_assignable

//  is_copy_assignable

//  is_trivially_copy_assignable
template <class T>
struct is_trivially_copy_assignable:
    public is_trivially_assignable <
        typename add_lvalue_reference<T>::type,
        const typename add_lvalue_reference<T>::type
    >
{};

//  is_nothrow_copy_assignable

//  is_move_assignable

//  is_trivially_move_assignable

//  is_nothrow_move_assignable

//  is_destructible

//  is_trivially_destructible
template <class T>
struct trivial_destructor_impl:
    public integral_constant<bool, is_scalar<T>::value || is_reference<T>::value> {};

template <class T>
struct is_trivially_destructible:
    public trivial_destructor_impl<typename remove_all_extents<T>::type> {};

//  is_nothrow_destructible

//  has_virtual_destructor

///////////////////////////////////////////////////////////////////////////////
//  Property queries
///////////////////////////////////////////////////////////////////////////////

//  alignment_of
template <typename T>
struct alignment_of_impl
{
    T t;
    char c;
    alignment_of_impl();
};

#define STX_ALIGNMENT_OF(T) \
    ((sizeof(stx::alignment_of_impl<T>) > sizeof(T)) ? \
     (sizeof(stx::alignment_of_impl<T>) - sizeof(T)) : sizeof(T))

template <class T>
struct alignment_of: integral_constant<std::size_t, STX_ALIGNMENT_OF(T)> {};

//  rank
template <class T>
struct rank: integral_constant<std::size_t, 0> {};
 
template <class T>
struct rank<T[]>: integral_constant<std::size_t, rank<T>::value + 1> {};
 
template <class T, std::size_t N>
struct rank<T[N]>: integral_constant<std::size_t, rank<T>::value + 1> {};

//  extent
template <class T, unsigned N = 0>
struct extent: integral_constant<std::size_t, 0> {};

template <class T>
struct extent<T[], 0>: integral_constant<std::size_t, 0> {};
 
template <class T, unsigned N>
struct extent<T[], N>: integral_constant<std::size_t, extent<T, N-1>::value> {};
 
template <class T, std::size_t N>
struct extent<T[N], 0>: integral_constant<std::size_t, N> {};
 
template <class T, std::size_t I, unsigned N>
struct extent<T[I], N>: integral_constant<std::size_t, extent<T, N-1>::value> {};

///////////////////////////////////////////////////////////////////////////////
//  Type relationships
///////////////////////////////////////////////////////////////////////////////

//  is_same - defined earlier

//  is_base_of - not supported yet

//  is_convertible - not supported yet

///////////////////////////////////////////////////////////////////////////////
//  Type modifications
///////////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------------
//  Const-volatility specifiers
//-----------------------------------------------------------------------------

//  remove_const - defined earlier

//  remove_volatile - defined earlier

//  remove_cv - defined earlier

//  add_const
template <class T> struct add_const { typedef const T type; };

//  add_volatile
template <class T> struct add_volatile { typedef volatile T type; };

//  add_cv
template <class T>
struct add_cv {
    typedef typename add_volatile<typename add_const<T>::type>::type type;
};

//-----------------------------------------------------------------------------
//  References
//-----------------------------------------------------------------------------

//  remove_reference
template <class T> struct remove_reference      { typedef T type; };
template <class T> struct remove_reference<T&>  { typedef T type; };
#ifdef STX_HAS_RVALUE_REFERENCES
template <class T> struct remove_reference<T&&> { typedef T type; };
#endif

//  add_lvalue_reference

//  add_rvalue_reference
#ifdef STX_HAS_RVALUE_REFERENCES
template <class T> struct add_rvalue_reference                      { typedef T&& type; };
template <>        struct add_rvalue_reference<void>                { typedef void type; };
template <>        struct add_rvalue_reference<const void>          { typedef const void type; };
template <>        struct add_rvalue_reference<volatile void>       { typedef volatile void type; };
template <>        struct add_rvalue_reference<const volatile void> { typedef const volatile void type; };
#endif

//-----------------------------------------------------------------------------
//  Pointers
//-----------------------------------------------------------------------------

//  remove_pointer
template <class T> struct remove_pointer                    { typedef T type; };
template <class T> struct remove_pointer<T*>                { typedef T type; };
template <class T> struct remove_pointer<T* const>          { typedef T type; };
template <class T> struct remove_pointer<T* volatile>       { typedef T type; };
template <class T> struct remove_pointer<T* const volatile> { typedef T type; };

//  add_pointer
template < class T >
struct add_pointer {
    typedef typename remove_reference<T>::type* type;
};

//-----------------------------------------------------------------------------
//  Sign modifiers
//-----------------------------------------------------------------------------

//  make_signed - not supported yet

//  make_unsigned - not supported yet

//-----------------------------------------------------------------------------
//  Arrays
//-----------------------------------------------------------------------------

//  remove_extent

//  remove_all_extents

//-----------------------------------------------------------------------------
//  Miscellaneous transformations
//-----------------------------------------------------------------------------

//  aligned_storage


//  aligned_union

//  decay
/*
template <class T>
struct decay {
    typedef typename remove_reference<T>::type U;
    typedef typename conditional< 
        is_array<U>::value,
        typename remove_extent<U>::type*,
        typename conditional< 
            is_function<U>::value,
            typename add_pointer<U>::type,
            typename remove_cv<U>::type
        >::type
    >::type type;
};
*/

//  enable_if
template <bool B, class T = void>
struct enable_if {};
 
template <class T>
struct enable_if<true, T> { typedef T type; };

//  conditional
template <bool B, class T, class F>
struct conditional { typedef T type; };
 
template <class T, class F>
struct conditional<false, T, F> { typedef F type; };

//  common_type

//  underlying_type

//  result_of

#ifdef STX_HAS_RVALUE_REFERENCES

template <class T>
inline typename remove_reference<T>::type&&
move(T&& t)
{
    typedef typename remove_reference<T>::type U;
    return static_cast<U&&>(t);
}

#else

template <class T>
inline T&
move(T& t)
{
    return t;
}

#endif

} // namespace stx

#endif // STX_TYPE_TRAITS

